package sharemem

import (
	"encoding/json"
	"server_cluster/common/gdconf"
	"server_cluster/common/logger"
	"server_cluster/common/protoc/pb"
	"sync/atomic"
)

type FarmLand struct {
	SeedId  int32
	EndTime int64
}

func (f *FarmLand) MemInit() {
	f.SeedId = 0
	f.EndTime = 0
}

type GatherHistory struct {
	SeedId     int32
	GatherTime int64
}

func (g *GatherHistory) MemInit() {
	g.SeedId = 0
	g.GatherTime = 0
}

func (g *GatherHistory) IsUse() bool {
	return g.SeedId != 0
}

func (g *GatherHistory) Copy(v GatherHistory) {
	g.SeedId = v.SeedId
	g.GatherTime = v.GatherTime
}

type Farm struct {
	FarmLandList      [9]FarmLand
	GatherHistoryList [100]GatherHistory
}

func (f Farm) MarshalJSON() ([]byte, error) {
	dbFarm := struct {
		FarmLandList      []FarmLand
		GatherHistoryList []GatherHistory
	}{}
	for _, farmLand := range f.FarmLandList {
		dbFarm.FarmLandList = append(dbFarm.FarmLandList, farmLand)
	}
	for _, gatherHistory := range f.GatherHistoryList {
		if !gatherHistory.IsUse() {
			continue
		}
		dbFarm.GatherHistoryList = append(dbFarm.GatherHistoryList, gatherHistory)
	}
	data, err := json.Marshal(dbFarm)
	if err != nil {
		return nil, err
	}
	return data, nil
}

func (f *Farm) UnmarshalJSON(data []byte) error {
	dbFarm := struct {
		FarmLandList      []FarmLand
		GatherHistoryList []GatherHistory
	}{}
	err := json.Unmarshal(data, &dbFarm)
	if err != nil {
		return err
	}
	for index, farmLand := range dbFarm.FarmLandList {
		f.FarmLandList[index] = farmLand
	}
	for index, gatherHistory := range dbFarm.GatherHistoryList {
		f.GatherHistoryList[index] = gatherHistory
	}
	return nil
}

func (f *Farm) MemInit() {
	for i := 0; i < len(f.FarmLandList); i++ {
		f.FarmLandList[i].MemInit()
	}
	for i := 0; i < len(f.GatherHistoryList); i++ {
		f.GatherHistoryList[i].MemInit()
	}
}

func (f *Farm) GetFarmLandByIndex(index int32) *FarmLand {
	if int(index) >= len(f.FarmLandList) {
		return nil
	}
	return &(f.FarmLandList[index])
}

func (f *Farm) GetFarmGatherHistoryList() []*GatherHistory {
	list := make([]*GatherHistory, 0)
	for i := len(f.GatherHistoryList) - 1; i >= 0; i-- {
		if !f.GatherHistoryList[i].IsUse() {
			continue
		}
		list = append(list, &f.GatherHistoryList[i])
	}
	return list
}

func (f *Farm) AddFarmGatherHistory(seedId int32) {
	isFull := true
	for i := 0; i < len(f.GatherHistoryList); i++ {
		if !f.GatherHistoryList[i].IsUse() {
			isFull = false
			break
		}
	}
	if isFull {
		for i := 0; i < len(f.GatherHistoryList); i++ {
			if i == len(f.GatherHistoryList)-1 {
				f.GatherHistoryList[i].MemInit()
			} else {
				f.GatherHistoryList[i].Copy(f.GatherHistoryList[i+1])
			}
		}
	}
	for i := 0; i < len(f.GatherHistoryList); i++ {
		if !f.GatherHistoryList[i].IsUse() {
			f.GatherHistoryList[i].SeedId = seedId
			f.GatherHistoryList[i].GatherTime = MyShm.TimeNow
			return
		}
	}
}

type Factory struct {
	ItemId        int32
	CurrItemCount int32
	MaxItemCount  int32
	Speed         int32
	EndTime       int64
	LastTime      int64
}

func (f *Factory) MemInit() {
	f.ItemId = 0
	f.CurrItemCount = 0
	f.MaxItemCount = 0
	f.Speed = 0
	f.EndTime = 0
	f.LastTime = 0
}

type Building struct {
	BuildingUuid MyInt64
	BuildingId   int32
	PosX         float32
	PosY         float32
	PosZ         float32
	Level        int8
	Farm         Farm
	Factory      Factory
}

func (b *Building) SetSync() {
	atomic.AddUint32(&(b.BuildingUuid.Status), 1)
}

func (b *Building) IsUse() bool {
	return b.BuildingUuid.NewVal != 0 || b.BuildingUuid.OldVal != 0
}

func (b *Building) MemInit() {
	b.BuildingUuid.NewVal = 0
	b.BuildingUuid.OldVal = 0
	b.BuildingId = 0
	b.PosX = 0
	b.PosY = 0
	b.Level = 0
	b.Farm.MemInit()
	b.Factory.MemInit()
}

func (b *Building) Clear() {
	b.BuildingUuid.NewVal = 0
}

type UnlockBuilding struct {
	BuildingId int32
}

func (u *UnlockBuilding) IsUse() bool {
	return u.BuildingId != 0
}

func (u *UnlockBuilding) MemInit() {
	u.BuildingId = 0
}

type UserHome struct {
	Id                 MyInt32
	Uid                int64
	BuildingList       [BUILDING_MAX]Building
	UnlockBuildingList [50]UnlockBuilding
	Level              int8
	Exp                int32
}

func (h *UserHome) SetSync() {
	atomic.AddUint32(&(h.Id.Status), 1)
}

func (h *UserHome) MemInit() {
	for i := 0; i < len(h.BuildingList); i++ {
		h.BuildingList[i].MemInit()
	}
	for i := 0; i < len(h.UnlockBuildingList); i++ {
		h.UnlockBuildingList[i].MemInit()
	}
	h.Level = 0
	h.Exp = 0
}

func (h *UserHome) SetUserHomeLevel(level int8) {
	h.Level = level
}
func (h *UserHome) OnUserReg() {
	h.Id.NewVal = 1
	h.Level = 1
}

func (u *User) AddBuilding(buildingId int32, pos *pb.Pos) (*Building, bool) {
	for i := 0; i < len(u.UserHome.BuildingList); i++ {
		building := &(u.UserHome.BuildingList[i])
		if building.IsUse() {
			continue
		}
		building.MemInit()
		// 初始属性
		building.BuildingId = buildingId
		building.PosX = pos.X
		building.PosY = pos.Y
		building.PosZ = pos.Z
		building.Level = 1
		building.Farm.MemInit()
		building.Factory.MemInit()
		building.Factory.LastTime = MyShm.TimeNow
		building.BuildingUuid.NewVal = SnowflakeWorker.GenId()
		building.RefreshFactory()
		u.AddBuilding2Map(uint64(building.BuildingUuid.NewVal), building)
		return building, true
	}
	logger.Error("no empty building found")
	return nil, false
}

func (u *User) DestroyBuilding(buildingUuid uint64) bool {
	building := u.GetBuildingById(buildingUuid)
	if building == nil {
		logger.Error("building not found")
		return false
	}
	u.DeleteBuildingById(uint64(building.BuildingUuid.NewVal))
	building.Clear()
	return true
}

func (u *User) AddUnlockBuilding(buildingId int32) bool {
	if u.GetUnlockBuildingById(buildingId) != nil {
		logger.Error("unlock building already exist, buildingId: %v", buildingId)
		return false
	}
	for i := 0; i < len(u.UserHome.UnlockBuildingList); i++ {
		unlockBuilding := &(u.UserHome.UnlockBuildingList[i])
		if unlockBuilding.IsUse() {
			continue
		}
		unlockBuilding.BuildingId = buildingId
		u.AddUnlockBuilding2Map(unlockBuilding.BuildingId, unlockBuilding)
		u.UserHome.SetSync()
		return true
	}
	logger.Error("no empty building unlock id found")
	return false
}

func (h *UserHome) AddExp(exp int32) {
	h.Exp += exp
	for {
		homeLevelConfig := gdconf.GetHomeLevelConfig(int32(h.Level))
		if homeLevelConfig == nil {
			break
		}
		if gdconf.GetHomeLevelConfig(int32(h.Level+1)) == nil {
			break
		}
		if h.Exp < 0 {
			break
		}
		if h.Exp < homeLevelConfig.Exp {
			break
		}
		h.Level++
		h.Exp -= homeLevelConfig.Exp
	}
	h.SetSync()
}

func (h *UserHome) SubExp(exp int32) {
	if exp < 0 {
		logger.Error("sub exp negative exp <0 exp: %v", exp)
		return
	}
	h.Exp = h.Exp - exp
	h.SetSync()
}

func (u *User) RefreshHomeFactory() {
	for _, building := range u.GetBuildingMap() {
		building.RefreshFactory()
	}
}

func (b *Building) RefreshFactory() {
	if b.Factory.CurrItemCount != 0 && b.Factory.CurrItemCount >= b.Factory.MaxItemCount {
		return
	}
	homeBuildingConfig := gdconf.GetHomeBuildingConfig(b.BuildingId)
	if homeBuildingConfig == nil {
		return
	}
	homeBuildingLevelGroupConfig := gdconf.GetHomeBuildingLevelGroupConfigByGroupAndLevel(homeBuildingConfig.BuildingLevelGroup, int32(b.Level))
	if homeBuildingLevelGroupConfig == nil {
		return
	}
	if homeBuildingLevelGroupConfig.BuildingType != gdconf.BuildingTypeFactory {
		return
	}
	b.Factory.ItemId = homeBuildingLevelGroupConfig.Param1
	b.Factory.MaxItemCount = homeBuildingLevelGroupConfig.Param3
	b.Factory.Speed = homeBuildingLevelGroupConfig.Param2
	b.Factory.EndTime = int64((b.Factory.MaxItemCount-b.Factory.CurrItemCount)/b.Factory.Speed*60) + b.Factory.LastTime
	if MyShm.TimeNow-b.Factory.LastTime < 60 {
		return
	}
	b.Factory.CurrItemCount += b.Factory.Speed * int32((MyShm.TimeNow-b.Factory.LastTime)/60)
	if b.Factory.CurrItemCount > b.Factory.MaxItemCount {
		b.Factory.CurrItemCount = b.Factory.MaxItemCount
	}
	b.Factory.LastTime = MyShm.TimeNow - (MyShm.TimeNow-b.Factory.LastTime)%60
	b.SetSync()
}

func (u *User) PacketPbBuildingList() []*pb.HomeBuildingData {
	pbBuildingList := make([]*pb.HomeBuildingData, 0)
	for _, building := range u.GetBuildingMap() {
		pbBuildingList = append(pbBuildingList, building.PacketPbBuilding(u))
	}
	return pbBuildingList
}

func (u *Building) PacketPbBuilding(user *User) *pb.HomeBuildingData {
	homeBuildingConfig := gdconf.GetHomeBuildingConfig(u.BuildingId)
	if homeBuildingConfig == nil {
		return nil
	}
	homeBuildingLevelGroupConfig := gdconf.GetHomeBuildingLevelGroupConfigByGroupAndLevel(homeBuildingConfig.BuildingLevelGroup, int32(u.Level))
	if homeBuildingLevelGroupConfig == nil {
		return nil
	}
	var pbFactoryData *pb.Factory = nil
	var pbFarmData *pb.Farm = nil
	switch homeBuildingLevelGroupConfig.BuildingType {
	case gdconf.BuildingTypeFactory:
		pbFactoryData = &pb.Factory{
			ItemId:        u.Factory.ItemId,
			CurrItemCount: u.Factory.CurrItemCount,
			EndTime:       u.Factory.EndTime,
			ServerTime:    MyShm.TimeNow,
		}
	case gdconf.BuildingTypeFarm:
		pbFarmLandList := make([]*pb.FarmLand, 0)
		pbGatherHistoryList := make([]*pb.GatherHistory, 0)
		for _, farmLand := range u.Farm.FarmLandList {
			pbFarmLandList = append(pbFarmLandList, &pb.FarmLand{
				SeedId:     farmLand.SeedId,
				EndTime:    farmLand.EndTime,
				ServerTime: MyShm.TimeNow,
			})
		}
		for _, gatherHistory := range u.Farm.GetFarmGatherHistoryList() {
			pbGatherHistoryList = append(pbGatherHistoryList, &pb.GatherHistory{
				SeedId:     gatherHistory.SeedId,
				GatherTime: gatherHistory.GatherTime,
				Count:      1,
				Uid:        user.GetUid(),
			})
		}
		pbFarmData = &pb.Farm{
			FarmLandList:      pbFarmLandList,
			GatherHistoryList: pbGatherHistoryList,
		}
	default:
	}
	return &pb.HomeBuildingData{
		BuildingUuid: uint64(u.BuildingUuid.NewVal),
		BuildingId:   u.BuildingId,
		Pos: &pb.Pos{
			X: u.PosX,
			Y: u.PosY,
			Z: u.PosZ,
		},
		Level:       int32(u.Level),
		FarmData:    pbFarmData,
		FactoryData: pbFactoryData,
	}
}
